package com.weilele.mvvm.base

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.graphics.Point
import android.graphics.drawable.ColorDrawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import com.weilele.mvvm.R
import com.weilele.mvvm.utils.safeGet
import com.weilele.mvvm.utils.activity.setWindowAlpha
import java.lang.ref.WeakReference


/**
 * 若红楼梦空，亦初心不变
 * 作者：thinker
 * 包名：com.yizisu.basemvvm.mvvm
 * 时间：2019/5/30 12:38
 * 描述：直接new出来的PopupWindom
 * 子类实现的话，请继承{@see BasePopupWindow}
 */
open class MvvmPopupWindow : PopupWindow {

    private var contextWrf: WeakReference<Context?>? = null
    val context: Context?
        get() {
            return when (val ctx = contextWrf?.get()) {
                is AppCompatActivity -> {
                    ctx
                }
                is ContextWrapper -> {
                    ctx.baseContext
                }
                else -> {
                    ctx
                }
            }
        }

    //创建的时候赋值，取消的时候恢复
    private var oldWindowAlpha = 1f

    constructor(context: Context?) : this(context, null)
    constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(
        context,
        attrs,
        defStyleAttr,
        0
    )

    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes) {
        initPopupWindow(context)
    }

    constructor() : this(
        width = ViewGroup.LayoutParams.MATCH_PARENT,
        height = ViewGroup.LayoutParams.WRAP_CONTENT
    )

    constructor(contentView: View?) : this(
        contentView = contentView,
        width = ViewGroup.LayoutParams.MATCH_PARENT,
        height = ViewGroup.LayoutParams.WRAP_CONTENT
    )

    constructor(width: Int, height: Int) : this(null, width, height)
    constructor(contentView: View?, width: Int, height: Int) : this(
        contentView,
        width,
        height,
        true
    )

    constructor(
        contentView: View?,
        width: Int,
        height: Int,
        focusable: Boolean
    ) : super(contentView, width, height, focusable) {
        initPopupWindow(contentView?.context)
    }

    private fun initPopupWindow(context: Context?) {
        checkContext(context)
        //点击外面popupWindow消失
        isOutsideTouchable = isCanceledOnTouchOutside()
        //popupWindow获取焦点
        isFocusable = true
        //popupWindow设置背景图
        setBackgroundDrawable(ColorDrawable())
    }

    override fun dismiss() {
        setWindowBackgroundAlpha(oldWindowAlpha)
        super.dismiss()
        contextWrf?.clear()
        contextWrf = null
    }


    override fun showAsDropDown(anchor: View?) {
        showAsDropDown(anchor, 0, 0)
    }

    override fun showAsDropDown(anchor: View?, xoff: Int, yoff: Int) {
        showAsDropDown(anchor, xoff, yoff, Gravity.TOP)
    }

    override fun showAsDropDown(anchor: View?, xoff: Int, yoff: Int, gravity: Int) {
        checkContext(anchor?.context)
        onPreShow(true, gravity)
        super.showAsDropDown(anchor, xoff, yoff, gravity)
    }

    override fun showAtLocation(parent: View?, gravity: Int, x: Int, y: Int) {
        checkContext(parent?.context)
        onPreShow(false, gravity)
        super.showAtLocation(parent, gravity, x, y)
    }

    /**
     *
     * 调用的时候，需要注意width和height的赋值问题
     * 显示在按钮的上方或者下方，根据是否显示可以自动更新位置
     *  isGravityEnd : true ,弹窗与view的右边缘对齐，可以通过设置xoff来横向偏移
     *  isGravityEnd : false ,弹窗与view的左边缘对齐，可以通过设置xoff来横向偏移
     *  isDropViewBottom:是否强制显示在下方
     *                 null：根据按钮位置自动判断（高于屏幕一半，显示按钮下方，否则上方），
     *                 true：总是显示按钮下方
     *                 false:总是显示按钮上方
     */
    fun showAsDropTopOrBottom(
        anchor: View,
        isGravityEnd: Boolean,
        xoff: Int = 0,
        yoff: Int = 0,
        isDropViewBottom: Boolean? = null
    ) {
        val anchorLocation = intArrayOf(0, 0)
        val screenPoint = Point()
        anchor.display.getRealSize(screenPoint)
        anchor.getLocationOnScreen(anchorLocation)
        val isShowBottom = isDropViewBottom ?: (anchorLocation[1] < screenPoint.y / 2)
        if (isShowBottom) {
            if (isGravityEnd) {
                animationStyle = R.style.PopupWindowScaleEndTopIn
                val x = screenPoint.x - anchorLocation[0] - anchor.width + xoff
                val y = anchorLocation[1] + anchor.height + yoff
                if (isShowing) {
                    update(x, y, width, height)
                } else {
                    showAtLocation(anchor, Gravity.TOP or Gravity.END, x, y)
                }
            } else {
                animationStyle = R.style.PopupWindowScaleStartTopIn
                val x = anchorLocation[0] + xoff
                val y = anchorLocation[1] + anchor.height + yoff
                if (isShowing) {
                    update(x, y, width, height)
                } else {
                    showAtLocation(anchor, Gravity.TOP or Gravity.START, x, y)
                }
            }
        } else {
            if (isGravityEnd) {
                animationStyle = R.style.PopupWindowScaleEndTopInToTop
                val x = screenPoint.x - anchorLocation[0] - anchor.width + xoff
                val y = screenPoint.y - anchorLocation[1] + yoff
                if (isShowing) {
                    update(x, y, width, height)
                } else {
                    showAtLocation(anchor, Gravity.BOTTOM or Gravity.END, x, y)
                }
            } else {
                animationStyle = R.style.PopupWindowScaleStartTopInToTop
                val x = anchorLocation[0] + xoff
                val y = screenPoint.y - anchorLocation[1] + yoff
                if (isShowing) {
                    update(x, y, width, height)
                } else {
                    showAtLocation(anchor, Gravity.BOTTOM or Gravity.START, x, y)
                }
            }
        }
    }

    /**
     * 显示在屏幕下方
     */
    fun showAtBottom() {
        showAtLocation(contentView, Gravity.BOTTOM, 0, 0)
    }

    /**
     * 显示在屏幕上方
     */
    fun showAtTop() {
        showAtLocation(contentView, Gravity.TOP, 0, 0)
    }

    /**
     * 调用显示方法之前会执行这个方法
     */
    protected open fun onPreShow(isAsDropDown: Boolean, gravity: Int) {
        setWindowBackgroundAlpha(getDialogDimAmount())
        if (animationStyle <= 0) {
            //未对动画赋值
            if (isNeedDialogAnim()) {
                //如果需要动画
                animationStyle = getDialogAnim(isAsDropDown, gravity)
            }
        }
    }

    /**
     * 检查并赋值上下文
     */
    internal fun checkContext(ctx: Context?) {
        ctx ?: return
        if (context == null) {
            contextWrf?.clear()
            contextWrf = WeakReference(ctx)
        }
    }

    /**
     * 设置背景是否变灰
     */
    private fun setWindowBackgroundAlpha(bgAlpha: Float) {
        checkContext(contentView?.context)
        context.safeGet<Activity>()?.run {
            oldWindowAlpha = window?.attributes?.alpha ?: 1f
            setWindowAlpha(bgAlpha)
        }
    }

    /**
     * 设置对话框背景透明度
     * 0-1
     */
    protected open fun getDialogDimAmount(): Float {
        return 0.6f
    }

    /**
     * 触摸外面是否可以取消
     */
    protected open fun isCanceledOnTouchOutside() = isOutsideTouchable


    /**
     * 是否需要默认,进入退出动画
     */
    protected open fun isNeedDialogAnim(): Boolean = true

    /**
     * 设置对话框进入退出动画
     * isAsDropDown:是否显示在某个view下方，即调用了showAsDropDown()方法
     * gravity：方向
     */
    @StyleRes
    protected open fun getDialogAnim(isAsDropDown: Boolean, gravity: Int): Int {
        return if (isAsDropDown) {
            when (gravity) {
                Gravity.START -> {
                    R.style.PopupWindowScaleStartTopIn
                }
                Gravity.END -> {
                    R.style.PopupWindowScaleEndTopIn
                }
                else -> {
                    R.style.PopupWindowTopScaleIn
                }
            }
        } else {
            when (gravity) {
                Gravity.BOTTOM -> {
                    R.style.PopupWindowBottomIn
                }
                Gravity.TOP -> {
                    R.style.PopupWindowTopIn
                }
                else -> {
                    R.style.AnimScaleCenter
                }
            }
        }
    }
}